/*file chiron src/browser/html.js */
/*preamble

    Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
    MIT License
    
    The license terms are stated in full in <license.rst> and at the end
    of all source files.

*/


include('base.js');
include('browser.js');
include('event.js');
include('environment.js');

exports.$ = function (element) {
    if (isInstance(element, String)) {
        element = document.getElementById(element);
    }
    return element;
};

/**

    XML
    ---

*/

/*** createElement
    creates or reuses a tag with a given name from the window
    document object.
*/

/*** destroyElement
    recycles an element of the window document.
*/

/*** getTagName
    returns the tag name of an XML tag element in
    ``lower_case``.
*/
exports.getTagName = function (element) {
    return lower(element.nodeName);
};

/*** getTagNameNs
    returns an ``Object`` with ``nsUri``
    and ``tagName`` attributes.
*/
exports.getTagNameNs = function (element) {
    var tagName = getTagName(element);
    var nsUri;
    args(tagName.split(':'), function (ns, tagName) {

        /* traverse up the DOM to find an namespace
         * URI for the given namespace */
        var at = element;
        while (at) {
            var namespaces = getNs(at);
            if (ns in namespaces) {
                nsUri = namespaces[ns];
                break;
            }
            at = at.parentNode;
        }

        return {
            'nsUri': nsUri,
            'tagName': tagName
        };
    });
};

/*** isTag
*/
exports.isTag = function (element) {
    return element.nodeType == 1;
};

/*** isText
*/
exports.isText = function (element) {
    return element.nodeType == 3;
};

/*** getText
*/
exports.getText = function (element) {
    return element.nodeValue;
};

/*** getChildren
    gets a ``List`` of the current children of an element.
*/
exports.getChildren = function (element) {
    return range(element.childNodes.length).each(function (n) {
        return element.childNodes.item(n);
    });
};

/*** setChildren 
    sets the children of an element to a given list.
*/
exports.setChildren = function (element, children) {
    forEach(flatten(children), function (child) {
        if (isInstance(child, String) || isInstance(child, Number)) {
            child = document.createTextNode('' + child);
        }
        element.appendChild(child);
    });
};

/*** tag
*/
exports.tag = function (nodeName, attrs, children) {
    /*todo garbage collection, free listing */
    var element = document.createElement(nodeName);
    setAttrs(element, attrs);
    setChildren(element, children);
    return element;
};

/* from prototype.  translations of DOM Element attributes
 * to their standard forms, used by attribute accessors and
 * mutators */
/*todo consider whether these are necessary */
exports.translations = {
    'colspan':   'colSpan',
    'rowspan':   'rowSpan',
    'valign':    'vAlign',
    'datetime':  'dateTime',
    'accesskey': 'accessKey',
    'tabindex':  'tabIndex',
    'enctype':   'encType',
    'maxlength': 'maxLength',
    'readonly':  'readOnly',
    'longdesc':  'longDesc'
};

/*** getAttr */
/*todo take off with prototype code here */
exports.getAttr = operator(2, 'getAttr', function (element, key, defaultValue) {
    /* http://www.glennjones.net/Post/809/getAttributehrefbug.htm
    additionally: Safari/WebKit produces the same href behaviors as Mozilla/Firefox/Gecko
    */
    if (!hasAttr(element, key)) {
        if (arguments.length === 3) {
            return defaultValue;
        } else {
            throw new Error("AttrError");
        }
    }
    if (key == 'href' && isOpera) {
        warn('no reliable way to get the unresolved href text in Opera');
    }
    /* the 2 parameter, when relevant, instructs the browser
     * to explicitly render the text of the attribute instead of
     * an extrpolated value, as would be the case of href in
     * Explorer */
    return element.getAttribute(key, 2);
});

/*** getAttrs */
exports.getAttrs = operator(1, 'getAttrs', function (element) {
    return range(element.attributes.length).eachIter(function (i) {
        var attribute = element.attributes.item(i);
        return [attribute.name, attribute.value];
    }).sorted();
});

/*** setAttr */
exports.setAttr = operator(3, 'setAttr', function (element, key, value) {
    /* purge the namespace cache used by getNs and its users. */
    element.namespaces = undefined;
    if ((key == 'style' || key == 'styles') && !isInstance(value, String)) {
        setStyles(element, value);
    } else if (key == 'class') {
        setClasses(element, [value]);
    } else if (key == 'observe' || key == 'observes') {
        observes(element, value);
    } else if (key == 'innerHTML') {
        element.innerHTML = value;
    } else {
        element.setAttribute(key, value);
    }
});

/*** setAttrs */
exports.setAttrs = operator(2, 'setAttrs', function (element, attrs) {
    itemsIter(attrs).forEachArgs(function (key, value) {
        setAttr(element, key, value);
    });
});

/*** hasAttr */
/* specified in modules.js */
exports.hasAttr = operator(2, 'hasAttr', hasAttr);

/*** delAttr */
exports.delAttr = operator(2, 'delAttr', function (element, key) {
    element.removeAttribute(key);
});

/*** hasNs
    returns whether a namespace has been defined
    in the scope of a given element.
*/
exports.hasNs = operator(2, 'hasNs', function (element, ns) {
    var at = element;
    while (at) {
        var namespaces = getNs(at);
        if (!no(namespaces[namespace]))
            return true;
        at = at.parentNode;
    }
    return false;
});

/*** getNs
    returns an object containing mappings of URI's for
    XML namespaces to their abbreviations (``localName``)
    specified on a particular native ``Element``.
*/
exports.getNs = operator(1, 'getNs', function (element) {
    /* memoized */
    if (element.namespaces) return element.namespaces;
    var namespaces = {};
    if (element.attributes) {
        var attributes = element.attributes;
        forEach(range(attributes.length), function (index) {
            var node = attributes[index];
            var key = node.nodeName;
            var uri = node.nodeValue;
            if (key == 'xmlns') {
                namespaces[uri] = '';
            } else if (/^xmlns\:/.test(key)) {
                var name = key.substr(6);
                namespaces[uri] = name;
            }
        });
    }
    element.namespaces = namespaces;
    return namespaces;
});

/*** getAttrNs */
exports.getAttrNs = operator(3, 'getAttrNs', function (element, key, namespace) {
    var at = element;
    while (at) {
        var namespaces = getNs(at);
        if (namespaces[namespace] === '') {
            return getAttr(element, key);
        } else if (namespaces[namespace]) {
            return getAttr(element, namespaces[namespace] + ':' + key);
        }
        at = at.parentNode;
    }
});

/*** setAttrNs */
exports.setAttrNs = operator(4, 'setAttrNs', function (element, key, value, namespace) {
    var at = element;
    while (at) {
        var namespaces = getNs(at);
        if (namespaces[namespace] === '') {
            setAttr(element, key, value);
        } else if (namespaces[namespace]) {
            setAttr(element, namespaces[namespace] + ':' + key, value);
        }
        at = at.parentNode;
    }
    throw new Error("No namespace for " + enquote(namespace));
});

/*** hasAttrNs */
exports.hasAttrNs = operator(3, 'hasAttrNs', function (element, key, namespace) {
    var at = element;
    while (at != null) {
        var namespaces = getNs(at);
        if (namespaces[namespace] === '') {
            if (hasAttr(element, key)) return true;
        } else if (namespaces[namespace]) {
            if (hasAttr(element, namespaces[namespace] + ':' + key)) return true;
        }
        at = at.parentNode;
    }
    return false;
});

/*** delAttrNs */
exports.delAttrNs = operator(3, 'delAttrNs', function (element, key, namespace) {
    var at = element;
    while (at) {
        var namespaces = getNs(at);
        if (namespaces[namespace] === '') {
            delAttr(element, key);
        } else if (namespaces[namespace]) {
            delAttr(element, namespaces[namespace] + ':' + key);
        }
        at = at.parentNode;
    }
    throw new Error("No namespace for " + enquote(namespace));
});

/*** getContent
    get the underlying markup of an element.
*/
exports.getContent = function (element) {
    if (element.innerHTML !== undefined) {
        return element.innerHTML;
    } else {
        return 'log("innerHTML is not available");';
    }
};

/*** setContent */
exports.setContent = operator(2, 'setContent', function (element, value) {
    /*todo purge */
    element.innerHTML = value;
    return element;
});

/*** visit
    traverses the DOM tree starting with element, calling
    ``prefix`` on the element before its children, and ``postfix``
    on the element after its children.  ``postfix`` is optional.

    Usage::
        ``visit(element, prefix, [postfix])``
*/
exports.visit = function (element, prefix, postfix) {
    if (element.nodeType == 1 && prefix) prefix(element);
    var child = element.firstChild;
    while (child) {
        visit(child, prefix, postfix);
        child = child.nextSibling;
    }
    if (element.nodeType == 1 && postfix) postfix(element);
};

exports.prefixIter = function (element, elements) {
    if (no(elements))
        elements = list();
    elements.push(element);
    return Iter(function () {
        if (elements.bool()) {
            var element = elements.pop();
            if (element.nextSibling)
                elements.push(element.nextSibling);
            if (element.firstChild)
                elements.push(element.firstChild);
            return element;
        } else {
            throw stopIteration;
        }
    }).whereIter(function (element) {
        return element.nodeType == 1;
    });
};


/*** parseHtml
*/
var div = document.createElement('div');
exports.parseHtml = function (html) {
    div.innerHTML = html;
    return elementIter(div).next();
};

/*** entitiesToUnicode
    a native ``Object`` containing translations
    from HTML entities to Unicode characters.
    ``decodeEntities`` uses this mapping.

    from similie.mit.edu
*/
exports.entitiesToUnicode = {
    'nbsp': '\u00A0',
    'iexcl': '\u00A1',
    'cent': '\u00A2',
    'pound': '\u00A3',
    'curren': '\u00A4',
    'yen': '\u00A5',
    'brvbar': '\u00A6',
    'sect': '\u00A7',
    'uml': '\u00A8',
    'copy': '\u00A9',
    'ordf': '\u00AA',
    'laquo': '\u00AB',
    'not': '\u00AC',
    'shy': '\u00AD',
    'reg': '\u00AE',
    'macr': '\u00AF',
    'deg': '\u00B0',
    'plusmn': '\u00B1',
    'sup2': '\u00B2',
    'sup3': '\u00B3',
    'acute': '\u00B4',
    'micro': '\u00B5',
    'para': '\u00B6',
    'middot': '\u00B7',
    'cedil': '\u00B8',
    'sup1': '\u00B9',
    'ordm': '\u00BA',
    'raquo': '\u00BB',
    'frac14': '\u00BC',
    'frac12': '\u00BD',
    'frac34': '\u00BE',
    'iquest': '\u00BF',
    'Agrave': '\u00C0',
    'Aacute': '\u00C1',
    'Acirc': '\u00C2',
    'Atilde': '\u00C3',
    'Auml': '\u00C4',
    'Aring': '\u00C5',
    'AElig': '\u00C6',
    'Ccedil': '\u00C7',
    'Egrave': '\u00C8',
    'Eacute': '\u00C9',
    'Ecirc': '\u00CA',
    'Euml': '\u00CB',
    'Igrave': '\u00CC',
    'Iacute': '\u00CD',
    'Icirc': '\u00CE',
    'Iuml': '\u00CF',
    'ETH': '\u00D0',
    'Ntilde': '\u00D1',
    'Ograve': '\u00D2',
    'Oacute': '\u00D3',
    'Ocirc': '\u00D4',
    'Otilde': '\u00D5',
    'Ouml': '\u00D6',
    'times': '\u00D7',
    'Oslash': '\u00D8',
    'Ugrave': '\u00D9',
    'Uacute': '\u00DA',
    'Ucirc': '\u00DB',
    'Uuml': '\u00DC',
    'Yacute': '\u00DD',
    'THORN': '\u00DE',
    'szlig': '\u00DF',
    'agrave': '\u00E0',
    'aacute': '\u00E1',
    'acirc': '\u00E2',
    'atilde': '\u00E3',
    'auml': '\u00E4',
    'aring': '\u00E5',
    'aelig': '\u00E6',
    'ccedil': '\u00E7',
    'egrave': '\u00E8',
    'eacute': '\u00E9',
    'ecirc': '\u00EA',
    'euml': '\u00EB',
    'igrave': '\u00EC',
    'iacute': '\u00ED',
    'icirc': '\u00EE',
    'iuml': '\u00EF',
    'eth': '\u00F0',
    'ntilde': '\u00F1',
    'ograve': '\u00F2',
    'oacute': '\u00F3',
    'ocirc': '\u00F4',
    'otilde': '\u00F5',
    'ouml': '\u00F6',
    'divide': '\u00F7',
    'oslash': '\u00F8',
    'ugrave': '\u00F9',
    'uacute': '\u00FA',
    'ucirc': '\u00FB',
    'uuml': '\u00FC',
    'yacute': '\u00FD',
    'thorn': '\u00FE',
    'yuml': '\u00FF',
    'quot': '\u0022',
    'amp': '\u0026',
    'lt': '\u003C',
    'gt': '\u003E',
    'OElig': '',
    'oelig': '\u0153',
    'Scaron': '\u0160',
    'scaron': '\u0161',
    'Yuml': '\u0178',
    'circ': '\u02C6',
    'tilde': '\u02DC',
    'ensp': '\u2002',
    'emsp': '\u2003',
    'thinsp': '\u2009',
    'zwnj': '\u200C',
    'zwj': '\u200D',
    'lrm': '\u200E',
    'rlm': '\u200F',
    'ndash': '\u2013',
    'mdash': '\u2014',
    'lsquo': '\u2018',
    'rsquo': '\u2019',
    'sbquo': '\u201A',
    'ldquo': '\u201C',
    'rdquo': '\u201D',
    'bdquo': '\u201E',
    'dagger': '\u2020',
    'Dagger': '\u2021',
    'permil': '\u2030',
    'lsaquo': '\u2039',
    'rsaquo': '\u203A',
    'euro': '\u20AC',
    'fnof': '\u0192',
    'Alpha': '\u0391',
    'Beta': '\u0392',
    'Gamma': '\u0393',
    'Delta': '\u0394',
    'Epsilon': '\u0395',
    'Zeta': '\u0396',
    'Eta': '\u0397',
    'Theta': '\u0398',
    'Iota': '\u0399',
    'Kappa': '\u039A',
    'Lambda': '\u039B',
    'Mu': '\u039C',
    'Nu': '\u039D',
    'Xi': '\u039E',
    'Omicron': '\u039F',
    'Pi': '\u03A0',
    'Rho': '\u03A1',
    'Sigma': '\u03A3',
    'Tau': '\u03A4',
    'Upsilon': '\u03A5',
    'Phi': '\u03A6',
    'Chi': '\u03A7',
    'Psi': '\u03A8',
    'Omega': '\u03A9',
    'alpha': '\u03B1',
    'beta': '\u03B2',
    'gamma': '\u03B3',
    'delta': '\u03B4',
    'epsilon': '\u03B5',
    'zeta': '\u03B6',
    'eta': '\u03B7',
    'theta': '\u03B8',
    'iota': '\u03B9',
    'kappa': '\u03BA',
    'lambda': '\u03BB',
    'mu': '\u03BC',
    'nu': '\u03BD',
    'xi': '\u03BE',
    'omicron': '\u03BF',
    'pi': '\u03C0',
    'rho': '\u03C1',
    'sigmaf': '\u03C2',
    'sigma': '\u03C3',
    'tau': '\u03C4',
    'upsilon': '\u03C5',
    'phi': '\u03C6',
    'chi': '\u03C7',
    'psi': '\u03C8',
    'omega': '\u03C9',
    'thetasym': '\u03D1',
    'upsih': '\u03D2',
    'piv': '\u03D6',
    'bull': '\u2022',
    'hellip': '\u2026',
    'prime': '\u2032',
    'Prime': '\u2033',
    'oline': '\u203E',
    'frasl': '\u2044',
    'weierp': '\u2118',
    'image': '\u2111',
    'real': '\u211C',
    'trade': '\u2122',
    'alefsym': '\u2135',
    'larr': '\u2190',
    'uarr': '\u2191',
    'rarr': '\u2192',
    'darr': '\u2193',
    'harr': '\u2194',
    'crarr': '\u21B5',
    'lArr': '\u21D0',
    'uArr': '\u21D1',
    'rArr': '\u21D2',
    'dArr': '\u21D3',
    'hArr': '\u21D4',
    'forall': '\u2200',
    'part': '\u2202',
    'exist': '\u2203',
    'empty': '\u2205',
    'nabla': '\u2207',
    'isin': '\u2208',
    'notin': '\u2209',
    'ni': '\u220B',
    'prod': '\u220F',
    'sum': '\u2211',
    'minus': '\u2212',
    'lowast': '\u2217',
    'radic': '\u221A',
    'prop': '\u221D',
    'infin': '\u221E',
    'ang': '\u2220',
    'and': '\u2227',
    'or': '\u2228',
    'cap': '\u2229',
    'cup': '\u222A',
    'int': '\u222B',
    'there4': '\u2234',
    'sim': '\u223C',
    'cong': '\u2245',
    'asymp': '\u2248',
    'ne': '\u2260',
    'equiv': '\u2261',
    'le': '\u2264',
    'ge': '\u2265',
    'sub': '\u2282',
    'sup': '\u2283',
    'nsub': '\u2284',
    'sube': '\u2286',
    'supe': '\u2287',
    'oplus': '\u2295',
    'otimes': '\u2297',
    'perp': '\u22A5',
    'sdot': '\u22C5',
    'lceil': '\u2308',
    'rceil': '\u2309',
    'lfloor': '\u230A',
    'rfloor': '\u230B',
    'lang': '\u2329',
    'rang': '\u232A',
    'loz': '\u25CA',
    'spades': '\u2660',
    'clubs': '\u2663',
    'hearts': '\u2665',
    'diams': '\u2666'
};

/*** decodeEntities
    translates a string that contains HTML entities into 
    the same string using Unicode characters instead.

    from similie.mit.edu
*/
var decodeEntitiesRegexp = /&(\w+?);/;
exports.decodeEntities = function (string) {
    while (decodeEntitiesRegexp.test(string)) {
        var match = string.match(decodeEntitiesRegexp);
        string = string.replace(decodeEntitiesRegexp, entitiesToUnicode[match[1]]);
    }
    return string;
};


/**

    Style
    -----

*/

/*** getStyle */
/* from prototype.js */
if (isIE) {
    exports.getStyle = function (element, key) {
        key = key == 'float' ? 'styleFloat' : styleCamel(key);
        var value = element.style[key];
        if (!value && element.currentStyle) value = element.currentStyle[style];
        if (key == 'opacity') {
            value = (
                getStyle(element, 'filter') || ''
            ).match(/alpha\(opacity=(.*)\)/);
            if (value) {
                if (value[1]) {
                    return parseFloat(value[1]) / 100;
                } else {
                    return 1.0;
                }
            }
        } else {
            if (value == 'auto') {
                if (
                    (key == 'width' || key == 'height') && 
                    getStyle(element, 'display') != 'none'
                ) {
                    return element['offset' + title(key)] + 'px';
                } else {
                    return undefined;
                }
            }
        }
        return value;
    };
} else {
    exports.getStyle = function (element, key) {
        key = key == 'float' ? 'cssFloat' : styleCamel(key);
        var value = element[key];
        if (!value) {
            var css = document.defaultView.getComputedStyle(element, null);
            value = css ? css[key] : null;
        }
        if (key == 'opacity') {
            return value ? parseFloat(value) : 1.0;
        }
        return value == 'auto' ? null : value;
    };
}
exports.getStyle = operator(2, 'getStyle', getStyle);

/*** setStyle */
/* from prototype.js */
exports.setStyle = operator(3, 'getStyle', function (element, key, value) {
    if (isInstance(value, Array) || isInstance(value, Iterable))
        value = join(value, ' ');
    if (key == 'opacity') {
        setOpacity(element, value)
    } else if (key == 'cursor') {
        if (value == 'pointer' || value == 'hand') {
            if (isIE) {
                element.style.cursor = 'hand';
            } else {
                element.style.cursor = 'pointer';
            }
        } else {
            element.style.cursor = value;
        }
    } else {
        element.style[
            key == 'float' || key == 'cssFloat' ? (
                no(element.style.styleFloat) ? (
                    'cssFloat'
                ) : (
                    'styleFloat'
                )
            ) : (
                styleCamel(key)
            )
        ] = value;
    }
    return element;
});

/*** getStyles */
/*todo getStyles */
exports.getStyles = operator(1, 'getStyles', function (element) {
});

/*** setStyles */
exports.setStyles = operator(2, 'setStyles', function (element, styles) {
    itemsIter(styles).forEachArgs(function (key, value) {
        setStyle(element, key, value);
    });
    return element;
});

/*** getOpacity */
exports.getOpacity = operator(2, 'getOpacity', getStyle('opacity'));

/*** setOpacity
    sets an elements opacity to a value from 0 for transparent
    to 1 for opaque.
*/
if (!isIE) {
    exports.setOpacity = function (element, value) {
        element.style.opacity = value == 1 ? .99999 : value < 0.00001 ? 0 : value;
        return element;
    };
} else {
    exports.setOpacity = function (element, value) {
        var filter = getStyle(element, 'filter');
        var style = element.style;
        if (value == 1) {
            style.filter = filter.replace(/alpha\([^\)]*\)/gi,'');
        } else {
            if (value < 0.00001) {
                value = 0;
            }
            style.filter = (
                filter.replace(/alpha\([^\)]*\)/gi, '') +
                'alpha(opacity=' + (value * 100) + ')'
            );
        }
        return element;
    };
}
exports.setOpacity = operator(2, 'setOpacity', setOpacity);

/*** getClasses
    gets a ``List`` of class names for an element.
*/
exports.getClasses = operator(1, 'getClasses', function (element) {
    return split(element.className, /\s+/);
});

/*** setClasses
    sets the class names of an element to a given list or string.
*/
exports.setClasses = operator(2, 'setClasses', function (element, value) {
    element.className = join(value, ' ');
});

/*** hasClass
    returns whether or not the specified classes are a portion of the
    class list currently applied to the node.
*/
/* from dojo */
exports.hasClass = operator(2, 'hasClass', function(node, value){
    return ((" " + node.className + " ").indexOf(" " + value + " ") >= 0);
});

/*** insertClass
    adds the specified classes to the end of the class list on the
    passed node.
*/
/* from dojo */
exports.insertClass = operator(2, 'insertClass', function (node, value){
    var className = node.className;
    if ((" " + className + " ").indexOf(" " + value + " ") < 0) {
        node.className = className + (className ? ' ' : '') + value;
    }
});

/*** removeClass
    removes a class from a node.
*/
/* from dojo */
exports.removeClass = operator(2, 'removeClass', function (node, value) {
    node.className = node.className.replace(
        new RegExp('(^|\\s+)' + value + '(\\s+|$)'),
        "$1$2"
    );
});

/*** styleCamel
    converts a ``style-case`` name string to
    a ``camelCase`` name.  This includes converting
    experimental, browser-specific attributes like
    ``-moz-opacity`` to the corresponding
    ``MozOpacity`` title-case names.
*/
exports.styleCamel = function (name) {
    return name.replace(/\-(\w)/g, function(ignore, letter) {
        return letter.toUpperCase();
    });
}

/**

    References
    ==========


*/


/*license

    Legal
    =======
    
    Chiron is a component of the Tale web-game project.
    
    See <credit.txt> for a complete list of
    contributions and their licenses.  All contributions are provided
    under permissive, non-viral licenses including MIT, BSD, Creative Commons
    Attribution 2.5, Public Domain, or Unrestricted.
    
    
    License
    =======
    
    Copyright (c) 2002-2008 Kris Kowal <http://cixar.com/~kris.kowal>
    MIT License
    
    
    MIT License
    -----------
    
    Permission is hereby granted, free of charge, to any person
    obtaining a copy of this software and associated documentation
    files (the "Software"), to deal in the Software without
    restriction, including without limitation the rights to use,
    copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the
    Software is furnished to do so, subject to the following
    conditions:
    
    The above copyright notice and this permission notice shall be
    included in all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
    OTHER DEALINGS IN THE SOFTWARE.

*/

